﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using OpenHardwareMonitor.Hardware;
using Newtonsoft.Json;
using System.Windows.Forms;

namespace OpenHardwareMonitor.Sever
{
    class Server
    {
        LinkedList<MobileConnection> connections;
        Computer computer;
        Thread nonBlockRead;
        public Boolean Running { get; set; }
        public int Port { get; set; }
        TcpListener tcpListener;


        public Server(Computer computer)
        {
            this.computer = computer;
            connections = new LinkedList<MobileConnection>();
        }

        public void toggle() {
            if (Running)
            {
                stopServer();
            }
            else {
                startServer();
            }
        }

        public void restart()
        {
            if (Running)
            {
                stopServer();
            }
            startServer();
            
        }

        public void stopServer(){
            if (Running)
            {
                try
                {
                    tcpListener.Stop();

                }
                catch (SocketException e)
                {
                    Console.Error.WriteLine(e.Message);
                }
            }
        }

        public void startServer()
        {
            cleanup();
            tcpListener = new TcpListener(Port);

            try
            {
                tcpListener.Start();
            }catch(SocketException e) {
                MessageBox.Show("Error starting server:\n"+e.Message);
            }
            Running = true;
            nonBlockRead = new Thread(new ThreadStart(this.waitForCon));
            nonBlockRead.Start();
        }
        private void waitForCon()
        {

            while (Running)
            {
                Socket socketForClient = null;
                try
                {
                    socketForClient = tcpListener.AcceptSocket();
                }catch(Exception exep){
                    return;
                }

                if (socketForClient.Connected)
                {
                    Console.WriteLine("Client connected");
                    connections.AddLast(new MobileConnection(socketForClient, computer));

                }
            }

        }

        public void cleanup()
        {

            foreach (MobileConnection con in connections)
            {
                if (!con.open)
                {
                    connections.Remove(con);
                }
            }

        }

        class MobileConnection
        {

            Thread thread;
            NetworkStream networkStream;
            System.IO.StreamWriter streamWriter;
            System.IO.StreamReader streamReader;
            public Boolean open { set; get; }
            Computer computer;
            Socket socketForClient;

            public MobileConnection(Socket socketForClient, Computer computer)
            {

                this.computer = computer;
                this.socketForClient = socketForClient;

                networkStream = new NetworkStream(socketForClient);
                streamWriter = new System.IO.StreamWriter(networkStream);
                streamReader = new System.IO.StreamReader(networkStream);

                thread = new Thread(new ThreadStart(this.communicate));
                thread.Start();
            }

            public void communicate()
            {

                open = true;

                while (open)
                {

                    String rec = streamReader.ReadLine();
                    Console.WriteLine(rec);

                    if (rec.CompareTo("close") == 0)
                    {
                        open = false;
                        socketForClient.Close();
                        Console.WriteLine("Closing Connection");

                    }
                    else
                    {

                        streamWriter.WriteLine(sendData());
                        streamWriter.Flush();
                    }

                }


            }

            public string sendData()
            {

                List<HardwareJSON> hardware = new List<HardwareJSON>();

                foreach (IHardware h in computer.Hardware)
                {
                    switch (h.HardwareType)
                    {
                        case HardwareType.CPU:
                            hardware.Add(prepareHardware(h, HardwareJSONType.CPU));
                            break;
                        case HardwareType.Mainboard:
                            hardware.Add(prepareMain(h));
                            break;
                        case HardwareType.GpuAti:
                            hardware.Add(prepareHardware(h, HardwareJSONType.Gpu));
                            break;
                        case HardwareType.GpuNvidia:
                            hardware.Add(prepareHardware(h, HardwareJSONType.Gpu));
                            break;
                        case HardwareType.HDD:
                            hardware.Add(prepareHardware(h, HardwareJSONType.HDD));
                            break;
                    }
                }

                return JsonConvert.SerializeObject(hardware);
            }

            private HardwareJSON prepareMain(IHardware h)
            {

                List<SubHardwareJSON> subs = new List<SubHardwareJSON>();
                List<String> names = new List<string>();
                foreach (IHardware sub in h.SubHardware)
                {
                    if (!names.Contains(sub.Name))
                    {
                        names.Add(sub.Name);
                        List<SensorJSON> sensores = new List<SensorJSON>();

                        foreach (ISensor s in sub.Sensors)
                        {
                            int temp = -1;
                            switch (s.SensorType)
                            {
                                case SensorType.Clock:
                                    temp = (int)SensorJSONType.Clock;
                                    break;
                                case SensorType.Load:
                                    temp = (int)SensorJSONType.Load;
                                    break;
                                case SensorType.Temperature:
                                    temp = (int)SensorJSONType.Temperature;
                                    break;
                                case SensorType.Voltage:
                                    temp = (int)SensorJSONType.Voltage;
                                    break;
                                default:
                                    break;
                            }
                            if (temp != -1)
                            {
                                SensorJSON sj = new SensorJSON { name = s.Name, max = s.Max, min = s.Min, type = (SensorJSONType)temp, value = s.Value };
                                sensores.Add(sj);
                            }
                        }
                        subs.Add(new SubHardwareJSON { name = sub.Name, sList = sensores });
                    }
                }
                return new HardwareJSON { name = h.Name, type = HardwareJSONType.Mainboard, subList = subs };

            }

            private HardwareJSON prepareHardware(IHardware h, HardwareJSONType tp)
            {
                List<SensorJSON> sensores = new List<SensorJSON>();

                foreach (ISensor s in h.Sensors)
                {
                    int temp = -1;
                    switch (s.SensorType)
                    {
                        case SensorType.Clock:
                            temp = (int)SensorJSONType.Clock;
                            break;
                        case SensorType.Load:
                            temp = (int)SensorJSONType.Load;
                            break;
                        case SensorType.Temperature:
                            temp = (int)SensorJSONType.Temperature;
                            break;
                        case SensorType.Voltage:
                            temp = (int)SensorJSONType.Voltage;
                            break;
                        default:
                            break;
                    }
                    if (temp != -1)
                    {
                        SensorJSON sj = new SensorJSON { name = s.Name, max = s.Max, min = s.Min, type = (SensorJSONType)temp, value = s.Value };
                        sensores.Add(sj);
                    }

                }

                return new HardwareJSON { name = h.Name, type = tp, sList = sensores };


            }

        }

    }


}
